#ifndef __COMMAND_SERVER_HPP__
#define __COMMAND_SERVER_HPP__

#include "Logger.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"

#include <iostream>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<pthread.h>
#include<functional>

static const int gbacklog = 8;
static const uint16_t gport = 8080;

using callback_t = std::function<std::string(const std::string&)>;

//./server serverport
class CommandServer
{
private:
    void HandlerIO(int sockfd , InetAddr addr)
    {
        while(true)
        {
            //只负责io
            char buffer[1024];
            int n = read(sockfd , buffer , sizeof(buffer)-1);
            //read 的返回值代表读取的数据个数，为0在网络中意味着客户端关闭了，-1则是读失败
            if(n > 0)
            {   
                //手动添加 \0
                buffer[n]=0;
                //获取到了发送的信息，可以交给上层去处理
                //回调出去
                std::string result = _cb(buffer);
                write(sockfd , result.c_str() , result.size());

                //将数据写回
                // std::string echo_string = "server scho# ";
                // echo_string += buffer;

                //debug
                // LOG(LogLevel::DEBUG) << addr.ToString() << "say: " <<buffer;
                // write(sockfd , echo_string.c_str() , echo_string.size());

            }
            else if(n==0)
            {
                LOG(LogLevel::INFO) << "client-" << addr.ToString() << " quit , me too , close fd: " << sockfd;
                break;
            }
            else
            {
                LOG(LogLevel::WARNING) << "read client " << addr.ToString() << " error , sockfd :" << sockfd;
                break;
            }
        }

        //只要退出循环均需要关闭文件描述符
        close(sockfd);
    }
public:
    CommandServer(callback_t cb , uint16_t port = gport) : _port(port),_cb(cb)
    {
    }
    ~CommandServer() {}
    // 初始化
    void Init() // 创建套接字、bind 、监听
    {
        // 创建套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0); // tcp -> 面向字节流,返回文件描述符
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "创建套接字失败";
            exit(SOCKET_CREATE_ERR);
        }
        LOG(LogLevel::INFO) << "创建套接字成功";

        // 将套接字与服务器的ip port 进行绑定
        InetAddr local(_port);
        // 成功返回0，失败返回-1
        if (bind(_listensockfd, local.Addr(), local.Length()) < 0)
        {
            LOG(LogLevel::FATAL) << "绑定套接字失败";
            exit(SOCKET_BIND_ERR);
        }
        LOG(LogLevel::INFO) << "创绑定套接字成功";

        // 监听,listen 成功返回0，失败返回-1
        if (listen(_listensockfd, gbacklog) != 0) // 第二个参数为全连接队列个数-1，一般设定为8 16 ……
        {
            LOG(LogLevel::FATAL) << "监听套接字失败";
            exit(SOCKET_LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "监听定套接字成功";
    }

    class ThreadData
    {
    public:
        ThreadData(int sockfd , InetAddr addr , CommandServer* self)
        :_sockfd(sockfd),_addr(addr),_self(self)
        {}

        int _sockfd;
        InetAddr _addr;
        CommandServer* _self;//调用HanderIO
    };

    static void *Routine(void* args)
    {
        ThreadData* self = static_cast<ThreadData*>(args);
        //让线程分离
        pthread_detach(pthread_self());
        //调用HandlerIO
        self->_self->HandlerIO(self->_sockfd , self->_addr);

        return (void*)0;
    }
    // 启动 ,服务器是一个死循环
    void Start() // 接收连接、读、写;获取连接失败重新获取就是了，服务器不会退出
    {
        while (true)
        {
            // accept,成功返回文件描述符，失败返回-1；_listensockfd 只是用来获取连接，不负责通信
            // 获取连接的同时，还需要获得发送发的网络地址信息
            struct sockaddr_in peer;      // 输出型参数
            socklen_t len = sizeof(peer); // 输入输出型参数
            int sockfd = accept(_listensockfd, (struct sockaddr *)&peer, &len);
            if (sockfd < 0)
            {
                LOG(LogLevel::ERROR) << "获取连接失败";
                continue;
            }
            LOG(LogLevel::INFO) << "获取连接成功";
            //走到此处说明获取连接成功，服务器从文件描述符冲读、写
            InetAddr clientaddr(peer);

            //单执行流
            HandlerIO(sockfd , clientaddr);
            //创建线程
            pthread_t tid;
            //Routine函数返回值参数均需为void* 类型，所以必为static 成员函数，但静态成员函数无法直接访问非静态成员方法、函数
            ThreadData *td = new ThreadData(sockfd , clientaddr , this);
            pthread_create(&tid , nullptr , Routine , (void*)td);

        }
    }

private:
    int _listensockfd;
    uint16_t _port;
    callback_t _cb;
};

#endif